package com.xiaobawang.knowledge.spider.util;

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.font.FontProvider;
import org.apache.commons.lang3.StringUtils;
import org.docx4j.Docx4J;
import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.springframework.core.io.ClassPathResource;


import java.awt.*;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author synhard
 * @description 文件工具类
 * @date 2025/3/29 15:35
 * @email songxinhang2023@e-chinalife.com
 * @id 18101119
 * @phone 13001321080
 */
public class FileUtil {

    /**
     * 将字节数组集合按30MB分卷压缩
     *
     * @param files        文件字节数组集合
     * @param maxSizeBytes 单个压缩包最大字节数（如30MB=30*1024*1024）
     * @return 生成的压缩包文件集合
     */
    public static List<File> splitIntoZipFiles(List<byte[]> files, List<String> fileNames, long maxSizeBytes) throws IOException {
        List<File>      zipFiles       = new ArrayList<>();
        File            currentZipFile = null;
        ZipOutputStream zos            = null;
        long            currentSize    = 0;

        for (int i = 0; i < files.size(); i++) {
            byte[] data     = files.get(i);
            String fileName = fileNames.get(i);

            // 预估条目大小：原始数据大小 + 固定500字节元数据（文件名、头信息等）
            long entrySizeEstimate = data.length + 500;

            // 触发新压缩包的条件：
            // 1. 当前无压缩包
            // 2. 当前压缩包大小 + 新条目预估大小超过阈值
            if (currentZipFile == null || currentSize + entrySizeEstimate > maxSizeBytes) {
                if (zos != null) {
                    zos.close();
                    zipFiles.add(currentZipFile);
                }
                currentZipFile = File.createTempFile("zip-part-", ".zip");
                zos = new ZipOutputStream(Files.newOutputStream(currentZipFile.toPath()));
                currentSize = 0;  // 重置计数器
            }

            // 写入条目到压缩包
            ZipEntry zipEntry = new ZipEntry(fileName);
            zos.putNextEntry(zipEntry);
            zos.write(data);
            zos.closeEntry();
            zos.flush();

            // 更新当前压缩包预估大小（累加）
            currentSize += entrySizeEstimate;
        }

        // 处理最后一个未关闭的压缩包
        if (zos != null) {
            zos.close();
            zipFiles.add(currentZipFile);
        }

        return zipFiles;
    }

    /**
     * html转pdf
     *
     * @param inputStream  输入流
     * @param outputStream 输出流
     * @date : 2021/1/15 14:07
     */
    public static void convertToPdf(InputStream inputStream, OutputStream outputStream) throws IOException {

        PdfWriter pdfWriter = new PdfWriter(outputStream);
        PdfDocument pdfDocument = new PdfDocument(pdfWriter);
        //设置为A4大小
        pdfDocument.setDefaultPageSize(PageSize.A4);

        //添加中文字体支持
        ConverterProperties properties = new ConverterProperties();
        FontProvider fontProvider = new FontProvider();


        ClassPathResource resource = new ClassPathResource("simsun.ttf");
        InputStream           is       = resource.getInputStream();
        PdfFont microsoft = PdfFontFactory.createFont(is.readAllBytes(), PdfEncodings.IDENTITY_H, false);
        fontProvider.addFont(microsoft.getFontProgram(), PdfEncodings.IDENTITY_H);

        properties.setFontProvider(fontProvider);
        //        读取Html文件流，查找出当中的&nbsp;或出现类似的符号空格字符
        inputStream = readInputStrem(inputStream);
        if (inputStream != null) {
            //        生成pdf文档
            HtmlConverter.convertToPdf(inputStream, pdfDocument, properties);
            pdfWriter.close();
            pdfDocument.close();
        }
    }

    /**
     * 读取HTML 流文件，并查询当中的&nbsp;或类似符号直接替换为空格
     *
     * @param inputStream
     * @return
     */
    private static InputStream readInputStrem(InputStream inputStream) {
        // 定义一些特殊字符的正则表达式 如：
        String regEx_special = "\\&[a-zA-Z]{1,10};";
        try {
            //<1>创建字节数组输出流，用来输出读取到的内容
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //<2>创建缓存大小
            byte[] buffer = new byte[1024]; // 1KB
            //每次读取到内容的长度
            int len = -1;
            //<3>开始读取输入流中的内容
            while ((len = inputStream.read(buffer)) != -1) { //当等于-1说明没有数据可以读取了
                baos.write(buffer, 0, len);   //把读取到的内容写到输出流中
            }
            //<4> 把字节数组转换为字符串
            String content = baos.toString();
            //<5>关闭输入流和输出流
            //            inputStream.close();
            baos.close();
            //            log.info("读取的内容：{}", content);
            //            判断HTML内容是否具有HTML的特殊字符标记
            Pattern compile    = Pattern.compile(regEx_special, Pattern.CASE_INSENSITIVE);
            Matcher matcher    = compile.matcher(content);
            String  replaceAll = matcher.replaceAll("");
            //            log.info("替换后的内容：{}", replaceAll);
            //            将字符串转化为输入流返回
            InputStream stringStream = getStringStream(replaceAll);
            //<6>返回结果
            return stringStream;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 将一个字符串转化为输入流
     * @param sInputString 字符串
     * @return
     */
    public static InputStream getStringStream(String sInputString) {
        if (sInputString != null && !sInputString.trim().equals("")) {
            try {
                ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());
                return tInputStringStream;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * @author synhard
     * @description 将输出流转成文件，用于预览
     * @date 2025/6/20 14:21
     * @param outputStream 输出流
     * @param filePath 文件路径
     * @return java.io.File
     */
    public static File outputStreamToFile(OutputStream outputStream, String filePath) throws IOException {
        File file = new File(filePath);
        if (outputStream instanceof FileOutputStream) {
            // 如果已经是 FileOutputStream，直接获取文件
            return file;
        } else if (outputStream instanceof ByteArrayOutputStream) {
            // 如果是 ByteArrayOutputStream，将字节写入文件
            try (FileOutputStream fos = new FileOutputStream(file)) {
                ((ByteArrayOutputStream) outputStream).writeTo(fos);
            }
        } else {
            // 通用方法：复制流内容
            try (InputStream is = new ByteArrayInputStream(outputStream.toString().getBytes());
                 FileOutputStream fos = new FileOutputStream(file)) {
                byte[] buffer = new byte[1024];
                int length;
                while ((length = is.read(buffer)) != -1) {
                    fos.write(buffer, 0, length);
                }
            }
        }
        return file;
    }

    /**
     * @author synhard
     * @description 文件转字节数组
     * @date 2025/6/20 14:30
     * @param file 文件
     * @return byte[] 字节数组
     */
    public static byte[] fileToByteArray(File file) throws IOException {
        try (FileInputStream fis = new FileInputStream(file);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[8192]; // 8KB 缓冲区
            int len;
            while ((len = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        }
    }

    // 定义跨平台非法字符集（Windows + Unix）
    private static final Set<Character> INVALID_CHARS = new HashSet<>(Arrays.asList(
            '\\', '/', ':', '*', '?', '"', '<', '>', '|', '\0'
    ));

    // Windows保留文件名（如CON、PRN等）
    private static final Set<String> RESERVED_NAMES = new HashSet<>(Arrays.asList(
            "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5",
            "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4",
            "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
    ));

    public static String sanitizeFilename(String originalName) {
        if (originalName == null || originalName.isEmpty()) {
            return "unnamed";
        }

        // 步骤1：移除非法字符
        StringBuilder sb = new StringBuilder();
        for (char c : originalName.toCharArray()) {
            if (!INVALID_CHARS.contains(c)) {
                sb.append(c);
            } else {
                sb.append('_'); // 用下划线替换非法字符
            }
        }
        String sanitized = sb.toString();

        // 步骤2：处理保留文件名（Windows）
        String baseName = sanitized.split("\\.", 2)[0]; // 提取无后缀部分
        if (RESERVED_NAMES.contains(baseName.toUpperCase())) {
            sanitized = "_" + sanitized; // 添加前缀绕过限制
        }

        // 步骤3：移除首尾空白点和空格（Windows限制）
        sanitized = sanitized
                .replaceAll("^[\\s.]+|[\\s.]+$", "")
                .replaceAll("\\.{2,}", "."); // 避免多个连续点

        // 步骤4：路径规范化（处理 ./ 和 ../）
        try {
            Path path = Paths.get(sanitized).normalize(); // 消除冗余路径符号
            sanitized = path.toString();
        } catch (InvalidPathException ignored) {
            // 降级处理：截断至255字符
            sanitized = sanitized.substring(0, Math.min(sanitized.length(), 255));
        }

        return sanitized.isEmpty() ? "unnamed" : sanitized;
    }

}
